I conduct a STM (Strucutral Topic Model) estimation on a sample of 11,919 online news articles from seven news provider about domestic politics: Bild.de, DIE WELT, FOCUS ONLINE, SPIEGEL ONLINE, Stern.de, ZEIT ONLINE, Tagesschau.de. The articles are dated from 01.06.2017 to 31.12.2017 (German federal elections took place on 24th of September 2017.). I first extract all online articles using the the Eventregistry API. Then all articles from the section “domestic policy” are filtered by checking the URL structure.

To discover the latent topics in the corpus, the structural topic modeling (STM) developed by Roberts (2016) is applied. The STM is an unsupervised machine learning approach that models topics as multinomial distributions of words and documents as multinomial distributions of topics, allowing to incorporate external variables that effect both, topical content and topical prevalence. I will included the news provider as a control for both the topical content and the topical prevalence. Additional, the month an article was published is included as a control for the topical prevalence. The number of topics is set to 35.

Distribution of articles

The Figures below show the distribution of the number of articles from the respective news sources by date. There is a high peak around the federal elections on September, 24th.

ggsave({
  btw %>%
  ggplot(aes(site)) +
  geom_bar(fill=col[1], alpha= .8) +
  labs(x="", y="Number of articles") +
  theme(
      legend.position   = "none"
    )
  
},
filename = "../figs/bar.png", device = "png", 
width = 6, height = 4,
        dpi = 600)
plot1

plot1

ggsave({
  btw %>%
  group_by(date) %>%
  dplyr::summarise(obs = n()) %>%
  ggplot(aes(date, obs)) +
  geom_line(color=col[3]) +
  geom_vline(aes(xintercept=as.Date("2017-09-24")),
             linetype = 2, color=col[2]) +
  scale_color_manual(values = col) +
  labs(x="", y="number of articles",color="") +
  scale_x_date(breaks = date_breaks("1 month"), labels=date_format("%B", tz="CET")) +
  theme(
      legend.position   = "none",
      axis.title.x      = element_blank(),
      axis.text       = element_text(size = 8)
    )
},
filename = "../figs/timeline.png", device = "png",width = 6, height = 4,
dpi = 600
)
plot1

plot1

2. Model Results

Label topics

In order to improve readability and traceability, I assign a shorter name to the topics based on the most common words.

sagelabs <- sageLabels(stmOut, 20)

sagelabs$cov.betas[[3]]$problabels[,1:6]
##       [,1]          [,2]              [,3]            
##  [1,] "schulz"      "spd"             "martin"        
##  [2,] "merkel"      "schulz"          "duell"         
##  [3,] "grünen"      "özdemir"         "fdp"           
##  [4,] "diesel"      "maut"            "auto"          
##  [5,] "kohl"        "helmut"          "deutschland"   
##  [6,] "merkel"      "kanzlerin"       "angela"        
##  [7,] "wahl"        "wählen"          "bundestagswahl"
##  [8,] "seehof"      "csu"             "söder"         
##  [9,] "deutschland" "land"            "menschen"      
## [10,] "bundestag"   "abgeordnet"      "deutschen"     
## [11,] "steinmeier"  "wagenknecht"     "bundespräsid"  
## [12,] "deutschland" "abschiebung"     "flüchtling"    
## [13,] "schröder"    "gerhard"         "altkanzl"      
## [14,] "flüchtling"  "familiennachzug" "grünen"        
## [15,] "trump"       "deutschland"     "berlin"        
## [16,] "spd"         "koalit"          "union"         
## [17,] "weidel"      "afd"             "sendung"       
## [18,] "fdp"         "jamaika"         "grünen"        
## [19,] "ge"          "ti"              "ten"           
## [20,] "berlin"      "amri"            "menschen"      
## [21,] "bundeswehr"  "versuchung"      "einsatz"       
## [22,] "muslim"      "ditib"           "islam"         
## [23,] "cdu"         "niedersachsen"   "rot"           
## [24,] "cdu"         "tauber"          "bürgermeist"   
## [25,] "europa"      "eu"              "europäischen"  
## [26,] "bundestag"   "gesetz"          "spd"           
## [27,] "frauen"      "jahr"            "deutschland"   
## [28,] "prozent"     "afd"             "spd"           
## [29,] "afd"         "bundestag"       "gauland"       
## [30,] "zschäpe"     "prozess"         "gericht"       
## [31,] "bundeswehr"  "soldaten"        "leyen"         
## [32,] "hamburg"     "gipfel"          "polizei"       
## [33,] "türkei"      "erdogan"         "gabriel"       
## [34,] "polizei"     "berlin"          "polizisten"    
## [35,] "csu"         "seehof"          "cdu"           
## [36,] "jahr"        "hussein"         "kirch"         
## [37,] "baden"       "württemberg"     "stuttgart"     
## [38,] "afd"         "petri"           "partei"        
## [39,] "euro"        "deutschland"     "jahr"          
## [40,] "spahn"       "jen"             "cdu"           
##       [,4]                 [,5]              [,6]            
##  [1,] "wahlkampf"          "partei"          "gabriel"       
##  [2,] "kanzlerin"          "tv"              "thema"         
##  [3,] "grüne"              "göring"          "partei"        
##  [4,] "hendrick"           "autofahr"        "herstel"       
##  [5,] "einheit"            "kanzler"         "politik"       
##  [6,] "cdu"                "bundeskanzlerin" "union"         
##  [7,] "wahlkrei"           "cdu"             "wähler"        
##  [8,] "horst"              "marku"           "ministerpräsid"
##  [9,] "politik"            "politisch"       "parteien"      
## [10,] "jahr"               "wahlkrei"        "abgeordneten"  
## [11,] "linken"             "walter"          "frank"         
## [12,] "behörden"           "abgeschoben"     "jahr"          
## [13,] "nachrichten"        "kostenlo"        "hintergründ"   
## [14,] "deutschland"        "flüchtlingen"    "schutzstatu"   
## [15,] "usa"                "präsident"       "donald"        
## [16,] "schulz"             "große"           "groko"         
## [17,] "alic"               "zdf"             "partei"        
## [18,] "lindner"            "cdu"             "union"         
## [19,] "be"                 "gen"             "zent"          
## [20,] "anschlag"           "weihnachtsmarkt" "ani"           
## [21,] "piloten"            "strausberg"      "kinder"        
## [22,] "imam"               "muslimen"        "terror"        
## [23,] "grünen"             "spd"             "landtag"       
## [24,] "sachsen"            "kretschmer"      "politik"       
## [25,] "europäisch"         "macron"          "staaten"       
## [26,] "abstimmung"         "union"           "cdu"           
## [27,] "zahl"               "flüchtling"      "kinder"        
## [28,] "union"              "umfrag"          "fdp"           
## [29,] "partei"             "höcke"           "schäubl"       
## [30,] "nsu"                "richter"         "verurteilt"    
## [31,] "staatsanwaltschaft" "trupp"           "soldat"        
## [32,] "gewalt"             "scholz"          "polizisten"    
## [33,] "türkischen"         "türkisch"        "deutschland"   
## [34,] "beamten"            "täter"           "hamburg"       
## [35,] "union"              "obergrenz"       "horst"         
## [36,] "freiburg"           "alt"             "maria"         
## [37,] "kretschmann"        "auto"            "ministerpräsid"
## [38,] "fraktion"           "frauk"           "landtag"       
## [39,] "prozent"            "flüchtling"      "menschen"      
## [40,] "staatssekretär"     "kanzler"         "berlin"
topics <- matrix(c(1, "SPD, M.Schulz", 2, "A.Merkel vs. Schulz", 3, "B90/Die Grüne, coalition talks", 
                   4, "EU topics: Diesel, Glyphosat, toll", 5, "H.Kohl", 6, "A.Merkel", 7, "Federal Election results",
                   8, "Party congress (CSU and DIE LINKE)", 9, "General societal issues", 
                   10, "Information on Federal elections (e.g. Wahlomat, election programmes)",  
                   11, "W.Schäuble, F.Steinmeier", 12, "Refugees in GER", 13, "Auffangbecken ('Find us on Twitter')",
                   14, "B90/Die Grüne, climate/refugee policy", 15, "D.Trump, Israel, G20", 
                   16, "Great coalition, minority government", 17, "AfD, in media", 18, "Jamaica coalition", 
                   19, "Rubbish", 20, "Mix (Amri, Berlin, Court)", 21, "German armed forces, Mali, Helicopter", 
                   22, "Mix (Ditib, Weather, DB)", 23, "Elections in Niedersachsen", 24, "CDU, regional politics (Saxony)", 
                   25, "Mix (Europa, political talkshows)", 26, "Votes in the Bundestag", 
                   27, "Mix, Surveys, public statistics", 28, "Election polls", 
                   29, "AfD in parliament", 30, "Mix: Sexism, Court sentences, NSU trial", 31, "German armed forces, v.d.Leyen", 
                   32, "G20 in Hamburg", 33, "Diplomatic relations w Turkey, Russia", 
                   34, "Terror in GER, Left- & Right-wing", 35, "CDU/CSU H.Seehofer, Obergrenze",
                   36, "Church, Family", 37, "Car industry, Stuttgart (Mix)", 38, "AfD, F.Petry", 39, "Statistics, welfare policy",
                   40, "J.Spahn"), ncol=2, byrow=T)

# topics <- matrix(c(1, "SPD, M.Schulz", 2, "A.Merkel vs. Schulz", 3, "Jamaica coalition", 4, "Diesel scandal", 
#                    5, "H.Kohl", 6, "A.Merkel", 7, "Federal Election results", 8, "CSU, Seehofer, Söder", 
#                    9, "Deportation, radicalization, refugees", 10, "Parliament (Bundestag)", 
#                    11, "DIE LINKE, W.Schäuble", 12, "Refugees in GER", 13, "G.Schröder, IT Topics", 
#                    14, "B90/Die Grüne", 
#                    15, "D.Trump, antisemitism", 16, "Great coalition", 17, "AfD, in social media", 
#                    18, "Budget statistics, Welfare policy", 
#                    19, "Rubbish", 20, "Verfassungsschutz, Terror attacks", 21, "German armed forces, Drones, Mali", 
#                    22, "EU policies, Weather", 23, "Elections in Niedersachsen", 24, "CDU, social media", 
#                    25, "Political talkshows", 26, "Bundestag, Steinmeier, Maas", 
#                    27, "Surveys, public statistics", 28, "Election polls", 
#                    29, "AfD", 30, "Court sentences, NSU trial", 31, "German armed forces, v.d.Leyen", 
#                    32, "G20 in Hamburg", 33, "Diplomatic relations w Turkey, Russia", 
#                    34, "Terror attacks, polics reports", 35, "CDU/CSU H.Seehofer, Obergrenze"), ncol=2, byrow=T)

topics.df <- as.data.frame(topics) %>%
  transmute(topic_name = paste(V1, V2, sep=": "),
         topic = 1:k) 

Next, we can assign a topic to each document (topic with highest postertior gamma)

# Document-topic probabilities
stmOut %>% tidy("theta") -> theta

top_topics <- theta %>% 
  group_by(document) %>%
  mutate(therank = rank(-gamma)) %>%
  filter(therank == 1) %>%
  select(- therank)

btw.2 <- btw %>%
  mutate(document = articleID) %>%
  merge(.,top_topics, by="document") %>%
  ## Combine with Topic label
  merge(., topics.df, by="topic") %>%
  mutate(allocation = 1) 
How is the top-gamma value distributed among the corpus?
btw.2 %>%
  ggplot(aes(gamma)) +
  geom_density(color = col[3],
               fill = col[3], alpha=.7) +
  labs(x="Distribution of gamma value")

Inspect the unclear topics

btw.2 %>% filter(topic==13) %>% select(title, gamma, url) %>%
  arrange(desc(gamma)) %>%
  top_n(10) %>%
  htmlTable::htmlTable(align="l")
## Selecting by url
title gamma url
1 Sicherheitslücke in der WLAN-Verschlüsselung entdeckt 0.986555514350397 https://www.tagesschau.de/inland/wlan-sicherheitsluecke-101.html
2 Sicherheitslücke bei WPA2: Experten warnen vor Panikmache 0.981843740798477 https://www.tagesschau.de/inland/wlan-sicherheitsluecke-103.html
3 Ex-Kanzler: Gerhard Schröders neue Liebe und seine alten Gewohnheiten 0.975544656566877 https://www.welt.de/politik/deutschland/article168903005/Gerhard-Schroeders-neue-Liebe-und-seine-alten-Gewohnheiten.html
4 Kristina und Ole Schröder: „Wir hatten nur eine Stunde, um zu entscheiden“ - WELT 0.879729071603216 https://www.welt.de/politik/deutschland/article167406391/Wir-hatten-nur-eine-Stunde-um-zu-entscheiden.html
5 Bundestagswahl 2017: Die Homescreens von Christian Lindner, Hubertus Heil, Peter Tauber 0.842092319533292 https://www.welt.de/politik/deutschland/article168238989/Das-verraten-die-Handy-Homescreens-ueber-deutsche-Politiker.html
6 Kristina Schröder für Begrenzung von Kanzler-Amtszeit 0.804861768199714 https://www.welt.de/politik/deutschland/article167407642/Kristina-Schroeder-fuer-Begrenzung-von-Kanzler-Amtszeit.html
7 Ex-Familienministerin: Kristina Schröder erwartet drittes Kind - WELT 0.797760674625277 https://www.welt.de/politik/deutschland/article169984391/Kristina-Schroeder-erwartet-ihr-drittes-Kind.html
8 Niedersachsen-Wahl: Doris Schröder-Köpf zittert um SPD-Landtagsmandat - WELT 0.606438798992618 https://www.welt.de/politik/deutschland/article169527258/Doris-Schroeder-Koepf-bangt-um-ihre-politische-Karriere.html
9 Niedersachsen-Wahl: Doris Schröder-Köpf zittert um SPD-Landtagsmandat - WELT 0.605744816637034 https://www.welt.de/politik/deutschland/article169527258/Doris-Schroeder-Koepf-zittert-um-ihre-politische-Karriere.html
10 Wettbewerb: Bei der Kanzlerwahl fühlen sich Bewerber hintergangen - WELT 0.135179952524215 https://www.welt.de/politik/deutschland/article167643203/Bei-der-Kanzlerwahl-fuehlen-sich-Bewerber-hintergangen.html
btw.2 %>% filter(topic==10) %>% select(title, gamma, url) %>%
  arrange(desc(gamma)) %>%
  top_n(10) %>%
  htmlTable::htmlTable(align="l")
## Selecting by url
title gamma url
1 Bundestag: Schäubles #Twitterverbot erregt Abgeordneten-Gemüter - WELT 0.895107332200295 https://www.welt.de/politik/deutschland/article170909167/Schaeubles-Twitterverbot-erregt-Abgeordneten-Gemueter.html
2 Lohnentwicklung: Neuer Bundestag beschließt Diätenerhöhung für Abgeordnete - WELT 0.813444769430317 https://www.welt.de/politik/deutschland/article171557256/Neuer-Bundestag-beschliesst-Diaetenerhoehung-fuer-Abgeordnete.html
3 Angela Merkel: SPD macht der Kanzlerin schwere Vorwürfe - WELT 0.740265593621387 https://www.welt.de/politik/deutschland/article169988019/SPD-wirft-Merkel-Schuetzenhilfe-fuer-die-AfD-vor.html
4 Abstimmung im Bundestag: Keine Regierung, aber "Jamaika" funktioniert bereits - WELT 0.672775930442724 https://www.welt.de/politik/deutschland/article169988475/Keine-Regierung-aber-Jamaika-funktioniert-bereits.html
5 Regierung im Kreuzverhör: SPD-Antrag scheitert an "Jamaika" - WELT 0.66440301389251 https://www.welt.de/politik/deutschland/article169988475/SPD-Antrag-scheitert-an-Jamaika.html
6 Bundestag: AfD und Linkspartei kritisieren automatische Diätenerhöhung - WELT 0.653689124793428 https://www.welt.de/politik/deutschland/article171532121/AfD-und-Linkspartei-kritisieren-automatische-Diaetenerhoehung.html
7 Roman Müller-Böhm (FDP): Das fragwürdige Geschäftsgebaren des jüngsten Abgeordneten - WELT 0.49592270550205 https://www.welt.de/politik/deutschland/article170413550/Das-fragwuerdige-Geschaeftsgebaren-des-juengsten-Abgeordneten.html
8 Kinderfotos im Internet: Kinderhilfswerk kritisiert Eltern - WELT 0.464352503820377 https://www.welt.de/politik/deutschland/article170440180/Kinderbilder-im-Netz-Eltern-sind-sehr-unreflektiert.html
9 Wahlberechtigung & Wahlrecht 0.423283473400624 https://www.welt.de/politik/wahl/bundestagswahl/article116123416/Wahlberechtigung-Wahlrecht.html
10 Jürgen Trittin: "AfD-Claqueure ändern meine Haltung nicht" - WELT 0.288200067279037 https://www.welt.de/politik/deutschland/article171639958/War-Ihnen-der-Applaus-der-AfD-peinlich-Herr-Trittin.html

The plotQuote function allows to inspect die most common words of a topic for each covariate. Here I check for topic 3 (Jamaica coalition)

topic <- 40

plotQuote(c(paste(sagelabs$cov.betas[[1]]$problabels[topic,], collapse="\n"),
            paste(sagelabs$cov.betas[[2]]$problabels[topic,], collapse="\n"),
            paste(sagelabs$cov.betas[[3]]$problabels[topic,], collapse="\n"),
            paste(sagelabs$cov.betas[[4]]$problabels[topic,], collapse="\n"),
            paste(sagelabs$cov.betas[[5]]$problabels[topic,], collapse="\n"),
            paste(sagelabs$cov.betas[[6]]$problabels[topic,], collapse="\n"),
            paste(sagelabs$cov.betas[[7]]$problabels[topic,], collapse="\n")))

3.1. Topic proportions

In order to get an initial overview of the results, the figure below displays the topics ordered by their expected frequency across the corpus (left side of the Figure) and the expected proportion of a topic in public media minus the expected proportion of topic use in private media (right side of the Figure). Thus topics more associated with public media appear to the right of zero. To assign a label to each topic, I looked at the most frequent words in that topic and the most representative articles.

Keep only those articles, that are clear

keep <- c(1,2,3,4,5,6,7,12,14,15,16,17,18,21,23,24,28,29,31,32,33,34,35,38,40)
Here, I create a Dataframe that contains the columns means of theta (per topic and covariate level)
frequency <- as.data.frame(colMeans(stmOut$theta)) %>%
  mutate(frequency = colMeans(stmOut$theta),
         topic = topics[,1],
         topic_name=paste(topics[,1],topics[,2], 
                          sep=": ")) %>%
  filter(topic %in% keep)

freq <- tapply(stmOut$theta[,1], stmOut$settings$covariates$betaindex, mean)
freq <- as.data.frame(freq) %>% 
    mutate(site=stmOut$settings$covariates$yvarlevels,
           topic = 1)

for(i in 2:k) {
  freq1 <- tapply(stmOut$theta[,i], stmOut$settings$covariates$betaindex, mean)
  freq1 <- as.data.frame(freq1) %>% 
    transmute(site=stmOut$settings$covariates$yvarlevels,
           topic = i,
           freq = freq1)
  
  freq <- rbind(freq, freq1)
}

freq <- freq %>%
  left_join(., topics.df, by = "topic") %>%
  filter(topic %in% keep) %>%
  mutate(topic = topic_name) %>%
  left_join(., frequency %>% select(topic, frequency),
            by = "topic")

Next, we can plot the expected proportion of topic use in the overall corpus vs. the expected proportion of topic use for each medium.

p1 <- ggplot(frequency, aes(x=reorder(topic_name, frequency), y=frequency)) + 
    geom_col(fill=col[1], alpha=0.8) +
    coord_flip() +
    labs(x="", y="expected frequency") +
    theme(axis.text.x = element_text(size=8),
          axis.text.y = element_text(size=11),
          axis.title = element_text(size=10))

p1

p2 <- ggplot(freq, aes(reorder(topic_name,frequency), freq)) +
  geom_col(fill = col[3]) +
  #scale_fill_manual(values = col[1]) +
  coord_flip() +
  facet_wrap(~site, ncol = 7) +
  theme(
    #axis.text.y = element_blank(),
          axis.text.y = element_text(size=11),
          axis.title = element_text(size=10)) +
    labs(x="", y="expected frequency") 

p2 

3.2. Difference in topic prevalence

To identify which of these differences is significant, the conditional expectation of topic prevalence for given document characteristics can be estimated. More specifically, I estimate a linear model, where the documents are observations, the dependent variable is the posterior probability of a topic and the covariates are the metadata of documents (see equation below).

\[ \theta_d=\alpha+\beta_1x_{ownership}+\beta_2x_{month}+\epsilon \]

The estimateEffect() uses the method of composition to incorporate uncertainty in the dependent variable, drawing a set of topic proportions from the variational posterior repeated times and compute the coefficients as the average over all results.

effect <- estimateEffect(c(1:k) ~site+s(month), stmOut, 
                         metadata = out$meta, uncertainty = "None")

Here, I create a dataframe that contains the results of the estimation.

tables <- vector(mode="list", length = length(effect$topics))

for (i in seq_along(effect$topics)) {
  sims <- lapply(effect$parameters[[i]], function(x) stm:::rmvnorm(500, x$est, x$vcov))
  sims <- do.call(rbind, sims)
  est <- colMeans(sims)
  se <- sqrt(apply(sims,2, stats::var))
  tval <- est/se
  rdf <- nrow(effect$data) - length(est)
  p <- 2*stats::pt(abs(tval), rdf, lower.tail = FALSE)
  topic <- i
  
  coefficients <- cbind(topic, est, se, tval, p)
  rownames(coefficients) <- attr(effect$parameters[[1]][[1]]$est, "names") 
  colnames(coefficients) <- c("topic", "Estimate", "Std. Error", "t value", "p")
  tables[[i]] <- coefficients
}

out1 <- list(call=effect$call, topics=effect$topics, tables=tables)

coeff <- as.data.frame(do.call(rbind,out1$tables))

coeff <- coeff %>% 
  mutate(parameter = rownames(coeff),
         parameter = gsub("site", "", parameter),
         parameter = ifelse(parameter == "s(month)1", "1_July", parameter),
         parameter = ifelse(parameter == "s(month)2", "2_August", parameter),
         parameter = ifelse(parameter == "s(month)3", "3_September", parameter),
         parameter = ifelse(parameter == "s(month)4", "4_October", parameter),
         parameter = ifelse(parameter == "s(month)5", "5_November", parameter),
         parameter = ifelse(parameter == "s(month)6", "6_December", parameter),
         signifcant = ifelse(p <= 0.5,"yes","no")) %>%
  left_join(., topics.df, by="topic")

The following figure shows the regression results for each news page. The coefficients indicate the deviation from the base value of Bild.de.

p1 <- coeff %>% 
  filter(topic %in% keep) %>%
  filter(parameter %in% stmOut$settings$covariates$yvarlevels) %>%
  ggplot(aes(x = reorder(topic_name,topic, decreasing=F), y = Estimate, fill=factor(signifcant))) +
  geom_col() +
  scale_fill_manual(values = col[c(2,1)]) +
  scale_x_discrete(position = "top") +
  coord_flip() +
  facet_wrap(~parameter, ncol = 8, scales = "free_x") +
  labs(x="", fill="significant at the 5% level") +
  theme(legend.position = "top", 
        axis.text.y = element_text(size=9),
        axis.text.x = element_text(angle=90)) 

p1

ggsave(plot = p1, filename = "../figs/estimates.png", device = "png",width = 10, height = 7,
dpi = 600)

Sentiment analysis

The idea of Sentiment analysis is to determine the attitude of a writer through online text data toward certain topic or the overall tonality of a document.

Lexical or “bag-ofwords” approaches are commonly used. In that approach, the researcher provides pre-defined dictionaries (lists) of words associated with a given emotion, such as negativity. The target text is then deconstructed into individual words (or tokens) and the frequencies of words contained in a given dictionary are then calculated.

1. Load sentiment dictionary.

SentimentWortschatz, or SentiWS for short, is a publicly available German-language resource for sentiment analysis, opinion mining etc. It lists positive and negative polarity bearing words weighted within the interval of [-1; 1] plus their part of speech tag, and if applicable, their inflections. The current version of SentiWS (v1.8b) contains 1,650 positive and 1,818 negative words, which sum up to 15,649 positive and 15,632 negative word forms incl. their inflections, respectively. It not only contains adjectives and adverbs explicitly expressing a sentiment, but also nouns and verbs implicitly containing one.

sent <- c(
  # positive Wörter
  readLines("dict/SentiWS_v1.8c_Negative.txt",
            encoding = "UTF-8"),
  # negative Wörter
  readLines("dict/SentiWS_v1.8c_Positive.txt",
            encoding = "UTF-8")
) %>% lapply(function(x) {
  # Extrahieren der einzelnen Spalten
  res <- strsplit(x, "\t", fixed = TRUE)[[1]]
  return(data.frame(words = res[1], value = res[2],
                    stringsAsFactors = FALSE))
}) %>%
  bind_rows %>% 
  mutate(word = gsub("\\|.*", "", words) %>% tolower,
         value = as.numeric(value)) %>% 
  # manche Wörter kommen doppelt vor, hier nehmen wir den mittleren Wert
  group_by(word) %>% summarise(value = mean(value)) %>% ungroup

2. Apply the dictionary on the artciles.

We now take each word in each article and assign a sentiment value for that word. I only use articles that have been assigned a topic with a probability of over 90% (gamma > 0.9).

3. Calculate sentiment value

calculate weight:

\[ \text{weight} = \frac{\text{Total Articles, by site & topic}}{\text{Total Articles, by site}} \]

# Calculate weight
btw.2 <- btw.2 %>%
  
  # how many articles per site & topic?
  group_by(site, topic) %>%
  add_tally() %>%
  ungroup() %>%
  mutate(count = n) %>%
  select(-n) %>%
  
  # how many articles per site?
  group_by(site) %>%
  add_tally() %>%
  ungroup() %>%
  mutate(totalcount = n) %>%
  select(-n) %>%
  
  # calculate weight
  mutate(weight = count/totalcount)

Group by article ID and calculate the mean sentiment value of each article.

sentDF.red <- sentDF %>%

  # calculate weight and weighted sentiment score
  group_by(articleID) %>%
  summarise(sent_value = mean(sentiment)) %>%

  # leftjoin with btw
  left_join(., btw.2 %>% select(articleID, topic, topic_name,
                                title, url, site, gamma, weight),
            by="articleID") %>%
  mutate(weighted = sent_value*weight)
sentDF.red %>% group_by(topic_name) %>%
  top_n(5, gamma) %>%
  select(topic_name, gamma, title) %>%
  htmlTable::htmlTable(align="l")
topic_name gamma title
1 23: Elections in Niedersachsen 0.998952714878486 AfD Niedersachsen: Razzia bei AfD-Landeschef Hampel nach Betrugsvorwürfen
2 23: Elections in Niedersachsen 0.998482628437868 AfD: Die Neuen am rechten Rand
3 5: H.Kohl 0.998722295055571 Helmut Kohl: Privat ist privat
4 5: H.Kohl 0.998709363403315 Altkanzler privat: Als sich Helmut Kohl bei „In The Mood“ verliebte - WELT
5 32: G20 in Hamburg 0.998808524855738 G20-Gipfel in Hamburg: Coldplay, Shakira und Grönemeyer rufen zu friedlichem Protest auf
6 3: B90/Die Grüne, coalition talks 0.995333396896013 Junge Union Düsseldorf fordert "sofortigen Rücktritt" von Merkel
7 32: G20 in Hamburg 0.998651802733095 G20-Gipfel in Hamburg: Kehrtwende: Brasiliens Präsident will doch kommen
8 32: G20 in Hamburg 0.99865186428629 G20-Gipfel in Hamburg: Versteckte Botschaft an Trump: Melania besucht Klimarechenzentrum
9 3: B90/Die Grüne, coalition talks 0.995292964432186 FDP-Chef Lindner widerspricht Generalsekretärin: "Wiederaufnahme der Gespräche schließe ich aus"
10 5: H.Kohl 0.997010610211136 Kondolenzbuch für Helmut Kohl - Verabschieden Sie sich hier mit einem letzten Gruß vom Kanzler der Einheit
11 32: G20 in Hamburg 0.998550362285735 G20-Gipfel in Hamburg: Polizei räumt Park in Hamburg-Altona von Gipfel-Gegnern
12 14: B90/Die Grüne, climate/refugee policy 0.995637442283331 Schleswig-Holstein: Woran Jamaika zu scheitern droht. Eine Dokumentation - WELT
13 3: B90/Die Grüne, coalition talks 0.995958693239365 Trittin: FDP-Chef Lindner wollte Merkel "stürzen"
14 3: B90/Die Grüne, coalition talks 0.995867856827224 Auch die Grünen wollen mit alter Spitze in die Neuwahlen gehen
15 3: B90/Die Grüne, coalition talks 0.995098139396659 AfD fordert auch Termin beim Bundespräsidenten
16 32: G20 in Hamburg 0.998599218805647 G20-Gipfel in Hamburg: Saudischer König Salman sagt G20-Gipfel ab - warum er nicht nach Hamburg kommt
17 5: H.Kohl 0.996627059136445 Helmut Kohl: Verabschieden Sie sich hier mit einem letzten Gruß - FOCUS Online
18 5: H.Kohl 0.995726600155438 Helmut Kohl: Wir, die Kohls - das erste und letzte stern-Interview des Altkanzlers
19 29: AfD in parliament 0.995964044402692 Pressestimmen zum Parteitag: "Bei Machtspielchen hat die AfD längst Alt-Parteien-Niveau erreicht" - WELT
20 29: AfD in parliament 0.995859871234948 Pressestimmen zum Parteitag: "So sieht eine gespaltene Partei aus" - WELT
21 2: A.Merkel vs. Schulz 0.987559154968981 ZDF-Show „Klartext“: Bürgerin attackiert Angela Merkel - „Das ist ein Witz, was sie erzählen“
22 34: Terror in GER, Left- & Right-wing 0.995019689756393 Rock am Ring: Festivalgelände wegen Terrorwarnung geräumt - FOCUS Online
23 12: Refugees in GER 0.995804479787326 Islamistische Gefährder: Grundsatzurteil löst Abschiebewelle aus - WELT
24 12: Refugees in GER 0.995827872118817 Islamistische Gefährder: Grundsatzurteil löste Abschiebewelle aus - WELT
25 12: Refugees in GER 0.995707739209258 Terrorismusbekämpfung: Die Tücken bei der Abschiebung islamistischer Gefährder - WELT
26 4: EU topics: Diesel, Glyphosat, toll 0.991274404292735 Automobil-Industrie: Für den Diesel wird die Luft dünn | tagesschau.de
27 24: CDU, regional politics (Saxony) 0.987073781232799 Hass im Netz: "Jeder Fremde könnte theoretisch beteiligt sein"
28 21: German armed forces, Mali, Helicopter 0.990529506698554 Hubschrauberabsturz der Bundeswehr in Mali: Warum fiel der Tiger vom Himmel? - WELT
29 38: AfD, F.Petry 0.993500558199888 AfD: Machtkampf nach Muster
30 38: AfD, F.Petry 0.992825435294375 Alternative für Deutschland: Die Spalter sind gespalten
31 28: Election polls 0.992507578999007 SPON-Wahltrend: SPD fällt auf schlechtesten Wert seit Ende Januar
32 28: Election polls 0.99475966470698 SPON-Wahltrend: Kampf um Platz drei "too close to call"
33 24: CDU, regional politics (Saxony) 0.993486367228867 Grünen-Abgeordnete betroffen: Falscher Account twittert für Claudia Roth | faktenfinder.tagesschau.de
34 38: AfD, F.Petry 0.9932850381021 Alternative für Deutschland: Petry geht, die AfD steht
35 38: AfD, F.Petry 0.992666660253127 Nach Absage-Eklat - Schmeißen sie AfD-Chefin Petry jetzt raus?
36 15: D.Trump, Israel, G20 0.992926869842472 Nach BILD-Bericht und Protestwelle - Familienministerin streicht Geld für Mullah-Seminar - Politik Inland - Bild.de
37 33: Diplomatic relations w Turkey, Russia 0.991925851813241 „Russia Today bei Facebook an der Spitze“ - Diese Falschmeldung entzückte den Kreml - Politik Inland - Bild.de
38 28: Election polls 0.994151720964541 Bundestagswahl 2017: Anhänger von Union, FDP und Grüne für Jamaika-Koalition - SPIEGEL ONLINE
39 31: German armed forces, v.d.Leyen 0.993285168670801 Bundeswehr: Nüchtern stellt der Staatsanwalt Ursula von der Leyen bloß - WELT
40 2: A.Merkel vs. Schulz 0.992218411820406 ZDF-Sendung „Klartext“: Merkel im Verhör der kritischen Gebäudereinigerin
41 2: A.Merkel vs. Schulz 0.992215611311929 ZDF-Sendung „Klartext“: Merkel im Kreuzverhör der kritischen Gebäudereinigerin
42 1: SPD, M.Schulz 0.985374425468061 Martin Schulz: Auf der Achterbahn
43 4: EU topics: Diesel, Glyphosat, toll 0.991123611660426 Winfried Kretschmann: Fahrverbote und Abgasskandal - welche Rolle spielt der Grüne? - SPIEGEL ONLINE
44 12: Refugees in GER 0.995253462713123 Duisburger Schülerin: Bivsis Vater täuschte jahrelang den Staat - FOCUS Online
45 12: Refugees in GER 0.995253462713123 Duisburger Schülerin: Bivsis Vater täuschte jahrelang den Staat - FOCUS Online
46 17: AfD, in media 0.990860628212853 Altes Foto heizt Debatte an: Von Burkas und Bussitzen | faktenfinder.tagesschau.de
47 35: CDU/CSU H.Seehofer, Obergrenze 0.987851941330161 Rücktrittsforderungen: Nach Wahlpleite: CSU streitet um Seehofers Zukunft
48 21: German armed forces, Mali, Helicopter 0.992061815271865 Bundeswehr: "Tiger"-Absturz in Mali - Tödliche Turbulenzen - SPIEGEL ONLINE
49 14: B90/Die Grüne, climate/refugee policy 0.98529900040895 Boris Palmer: Jenseits der Hutschnur | ZEIT ONLINE
50 35: CDU/CSU H.Seehofer, Obergrenze 0.987470363114492 Nach Wahl-Debakel: CSU streitet nach Wahlpleite um Seehofers Zukunft
51 34: Terror in GER, Left- & Right-wing 0.993902079189982 Haftbefehl erlassen: Hamburger Messer-Angreifer als Islamist bekannt
52 1: SPD, M.Schulz 0.980782849727746 SPD: Eine Krise, keine Erklärung
53 34: Terror in GER, Left- & Right-wing 0.993995483849288 Wegen Mordverdacht in U-Haft: Messer-Angreifer von Hamburg war als Islamist bekannt
54 4: EU topics: Diesel, Glyphosat, toll 0.991787488014484 Diesel-Fahrverbote: Deutsche Umwelthilfe bereitet Klagen vor - Politik Inland - Bild.de
55 28: Election polls 0.990772285046733 Erprobtes «Kanzlermodell»: Politikforscher sagt Merkel-Sieg und Zweierkoalition voraus
56 28: Election polls 0.990814303653628 Erprobtes «Kanzlermodell»: «Kanzlerformel»: Merkel-Sieg und Zweierkoalition
57 7: Federal Election results 0.992232658380263 Analyse der Wahlsoftware: Experten sehen Sicherheitslücken in Bundestagswahl-Software
58 35: CDU/CSU H.Seehofer, Obergrenze 0.987733970325147 Zuwanderung: Grüne kritisieren Kompromiss zu Zuwanderung von CDU und CSU
59 21: German armed forces, Mali, Helicopter 0.992763813238514 Mali: Zwei Bundeswehr-Soldaten sterben bei Absturz
60 15: D.Trump, Israel, G20 0.993362732999715 Kompromissformel: G20 einig im Handelsstreit - Klima-Formulierung strittig
61 23: Elections in Niedersachsen 0.992311018803575 AfD in Niedersachsen: Parteivermögen für private Zwecke?
62 24: CDU, regional politics (Saxony) 0.986044776087429 Sachsen: Na dann gute Macht
63 34: Terror in GER, Left- & Right-wing 0.994067356589352 Festgenommene wieder frei: Musikfestival «Rock am Ring» geht nach Terroralarm weiter
64 34: Terror in GER, Left- & Right-wing 0.99410943545472 Festgenommene wieder frei: Musikfestival «Rock am Ring» nach Terroralarm fortgesetzt
65 29: AfD in parliament 0.990443810397996 Wer sind die ungebetenen Nachbarn von Björn Höcke?
66 2: A.Merkel vs. Schulz 0.988555674201864 Bundestagswahl 2017: TV-Duell im Kanzlerinnen-Modus - ein Unding
67 21: German armed forces, Mali, Helicopter 0.992265874510995 "Tiger"-Helikopter der Bundeswehr darf in Mali wieder fliegen
68 18: Jamaica coalition 0.991316136507795 Ackern bis zum Konsens: Jamaika-Sondierer und die Agrarpolitik
69 18: Jamaica coalition 0.987537248806074 Schleswig-Holstein: FDP, Grüne und CDU in Kiel einigen sich. Vorerst.
70 29: AfD in parliament 0.990937890469331 Umstrittener AfD-Angriff: Deutsche SPD-Politikerin "in Anatolien entsorgen": Gauland bleibt dabei
71 29: AfD in parliament 0.990865664218354 Umstrittener AfD-Angriff: Gauland bleibt dabei: Deutsche SPD-Politikerin "in Anatolien entsorgen"
72 15: D.Trump, Israel, G20 0.993029829473676 Jetzt doch! - ARD will plötzlich die Judenhass-Doku zeigen - Politik Inland - Bild.de
73 33: Diplomatic relations w Turkey, Russia 0.991182866160108 Sigmar Gabriel: Bundesregierung droht Türkei wegen Inhaftierung von Menschenrechtlern | ZEIT ONLINE
74 31: German armed forces, v.d.Leyen 0.992254443560462 Skandal-Kaserne in Pfullendorf - Angehörige gehen auf Ursula von der Leyen los! - Politik Inland - Bild.de
75 23: Elections in Niedersachsen 0.992128398521577 Ex-Grüne: Zu ihrer ersten CDU-Sitzung kam Twesten im blassrosa T-Shirt
76 14: B90/Die Grüne, climate/refugee policy 0.985185220919071 Acrylamid : Die Gefahr im Weihnachtskeks
77 23: Elections in Niedersachsen 0.99224104713194 Ex-Grüne: Zur ihrer ersten CDU-Sitzung kam Twesten im blassrosa T-Shirt - WELT
78 2: A.Merkel vs. Schulz 0.991365010594152 Angela Merkel gegen Martin Schulz - So läuft das TV-Duell ab - Politik Inland - Bild.de
79 15: D.Trump, Israel, G20 0.992699041421927 Hitler-Lob: Antisemitischer Vorfall bei Nahost-Diskussion an Berliner Schule - WELT
80 1: SPD, M.Schulz 0.985637906328594 25. Todestag Brandts: Wie viel Willy braucht die SPD?
81 31: German armed forces, v.d.Leyen 0.992111985203339 Bundeswehr: Neue Strafanzeige gegen Ursula von der Leyen
82 31: German armed forces, v.d.Leyen 0.992350822560548 Munster: Bundeswehrsoldaten wurden vor Kollaps auf Extra-Marsch geschickt - FOCUS Online
83 31: German armed forces, v.d.Leyen 0.992682072729025 Ermittlungen eingeleitet: Nazi-Verdacht gegen Bundeswehr-Elitetruppe KSK
84 33: Diplomatic relations w Turkey, Russia 0.989462427469104 Diplomatischer Konflikt: Gabriel richtet Brief an türkischstämmige Menschen in Deutschland - WELT
85 1: SPD, M.Schulz 0.98439665323211 Schulz im "Brigitte"-Talk: Liebe, Alkohol und Angela Merkel | tagesschau.de
86 35: CDU/CSU H.Seehofer, Obergrenze 0.988844964285463 Unionsstreit: Die Obergrenze gerät ins Wanken
87 15: D.Trump, Israel, G20 0.992174619312448 Antisemitischer Vorfall bei Nahost-Diskussion an Berliner Schule - WELT
88 38: AfD, F.Petry 0.992993620596133 Eklat nach der Bundestagswahl: Frauke Petry will aus der AfD austreten, Marcus Pretzell verlässt Landtagsfraktion
89 1: SPD, M.Schulz 0.98067565102393 SPD: Weiß wirklich Schulz, wie es geht?
90 33: Diplomatic relations w Turkey, Russia 0.990289283220291 Recep Tayyip Erdoğan: Gabriel sieht kaum Chance auf Freilassungen in der Türkei
91 16: Great coalition, minority government 0.989199583809463 Start für Sondierungen? - Heute muss sich die SPD entscheiden
92 17: AfD, in media 0.990396611462697 AfD hetzt mit Fälschung gegen Merkel und Flüchtlinge
93 17: AfD, in media 0.992839660273846 Nach Wortgefecht: AfD-Spitzenkandidatin Alice Weidel verlässt ZDF-Wahlsendung
94 7: Federal Election results 0.991501962822674 Wahlergebnis Nordfriesland – Dithmarschen Nord: Das Ergebnis im Wahlkreis 2 - Bundestagswahl 2017
95 16: Great coalition, minority government 0.987986016525283 SPD-Chef sorgt für Wirbel - Warum Schulz beim Thema GroKo so rumeiert
96 7: Federal Election results 0.991592750910957 Wahlergebnis Flensburg – Schleswig: Das Ergebnis im Wahlkreis 1 - Bundestagswahl 2017
97 4: EU topics: Diesel, Glyphosat, toll 0.986745968509984 Diesel-Skandal: Warum sich Winfried Kretschmann mit Jürgen Resch überworfen hat - SPIEGEL ONLINE
98 7: Federal Election results 0.991167453996159 Wahlergebnis Rendsburg-Eckernförde: Das Ergebnis im Wahlkreis 4 - Bundestagswahl 2017
99 14: B90/Die Grüne, climate/refugee policy 0.988003029173485 Fipronil: Grüne wollen Kennzeichnungspflicht für eierhaltige Lebensmittel | ZEIT ONLINE
100 14: B90/Die Grüne, climate/refugee policy 0.986172790947532 Krebserregender Stoff: BASF ruft Produkte zurück
101 16: Great coalition, minority government 0.986843897286116 SPD zu Regierungsbildung: Eine Art offene Ehe?
102 18: Jamaica coalition 0.987160716331331 Jamaika-Sondierungen: Einigkeit bei "schwarzer Null"
103 18: Jamaica coalition 0.987334268754948 Schleswig-Holstein: Jamaika-Verhandlungen unterbrochen - FDP sauer auf die Grünen - WELT
104 35: CDU/CSU H.Seehofer, Obergrenze 0.986473022256279 CSU und die Obergrenze - Über Namen lässt sich reden
105 17: AfD, in media 0.99237639693618 Kritik an Moderatorin Slomka: AfD-Kandidatin Weidel verlässt ZDF-Wahlsendung vorzeitig
106 33: Diplomatic relations w Turkey, Russia 0.991152270462974 Angriff in 28 Tweets: Türkischer Minister wirft Gabriel Rassismus vor | tagesschau.de
107 21: German armed forces, Mali, Helicopter 0.990436670842102 Hubschrauber-Absturz in Mali - „Tiger“ fiel im Sturzflug zu Boden - Politik Inland - Bild.de
108 24: CDU, regional politics (Saxony) 0.982874278864341 Minijob-Tweet: Empörung über Peter Taubers Bemerkung - FOCUS Online
109 40: J.Spahn 0.952586141618092 Grünen-Politikerin über CDU-Mann: "Dieser rechte schwule Jens Spahn"
110 18: Jamaica coalition 0.983969853660514 Jamaika-Sondierungen: Etwas Optimismus nach Runde eins
111 7: Federal Election results 0.991010331653298 Dresden II - Bautzen II: Das sind die Direktkandidaten im Wahlkreis 160 - Bundestagswahl 2017
112 17: AfD, in media 0.99044515999908 Kritik an Moderatorin Slomka: AfD-Kandidatin Weidel verlässt ZDF-Wahlsendung
113 4: EU topics: Diesel, Glyphosat, toll 0.984345934222474 Luftbelastung in Innenstädten: Grüne streiten über Diesel-Fahrverbot | tagesschau.de
114 24: CDU, regional politics (Saxony) 0.983974988645362 Twitter-Äußerung: "Was Ordentliches lernen" statt Minijobs - CDU-Generalsekretär schlittert in Shitstorm
115 16: Great coalition, minority government 0.98527207228688 Mächtiger SPD-Landesverband setzt Schulz unter Druck - und nennt GroKo-Bedingungen
116 16: Great coalition, minority government 0.98527207228688 Mächtiger SPD-Landesverband setzt Schulz unter Druck - und nennt GroKo-Bedingungen
117 6: A.Merkel 0.920824631648749 Minister Schmidt zu Glyphosat-Zulassung: "Habe die Entscheidung für mich getroffen" - SPIEGEL ONLINE - Politik
118 6: A.Merkel 0.949489101583499 Im Schengen-Raum: Merkel: Können auf Grenzkontrollen nicht verzichten
119 6: A.Merkel 0.941911022829094 Sicherheitspolitik: Merkel für Verlängerung von Grenzkontrollen
120 6: A.Merkel 0.945778865914079 Streit um Glyphosat-Votum: Kanzleramt erinnerte an Abmachung - Minister Schmidt ignorierte sie - SPIEGEL ONLINE - Politik
121 6: A.Merkel 0.953073352619941 Glyphosat-Entscheidung: Merkel rügt Alleingang von CSU-Agrarminister
122 40: J.Spahn 0.904888631934084 Grünen-Politikerin warnt vor CDU-Mann Jens Spahn

Plot Sentiment

1. by topic

p1 <- sentDF.red %>%
  group_by(topic_name, topic) %>%
  summarise(sent_value = mean(sent_value)) %>%
  ggplot(aes(reorder(topic_name, sent_value), sent_value)) +
  geom_col(fill=col[3], alpha=0.8) +
  coord_flip() +
  labs(x="", y="sentiment value (unweighted)",
       title = "sentiment value (unweighted)") +
  theme(
        axis.text.y = element_text(size = 10))

p2 <- sentDF.red %>%
  group_by(topic_name, topic) %>%
  summarise(weighted = mean(weighted),
            sent_value = mean(sent_value)) %>%
  ggplot(aes(reorder(topic_name, sent_value), weighted)) +
  geom_col(fill=col[1], alpha=0.8) +
  coord_flip() +
  labs(x="", y="sentiment value (weighted)",
       title = "sentiment value (weighted)") +
  theme(
        axis.text.y = element_blank())

p1 + p2

2.by site

p1 <- sentDF.red %>%
  group_by(site) %>%
  summarise(sent_value = mean(sent_value)) %>%
  ggplot(aes(reorder(site, sent_value), sent_value)) +
  geom_col(fill=col[3], alpha=0.8) +
  coord_flip() +
  labs(x="", y="sentiment value (unweighted)",
       title = "sentiment value (unweighted)") +
  theme(
        axis.text.y = element_text(size = 10))

p2 <- sentDF.red %>%
  group_by(site) %>%
  summarise(weighted = mean(weighted),
            sent_value = mean(sent_value)) %>%
  ggplot(aes(reorder(site, sent_value), weighted)) +
  geom_col(fill=col[1], alpha=0.8) +
  coord_flip() +
  labs(x="", y="sentiment value (weighted)",
       title = "sentiment value (weighted)") +
  theme(
        axis.text.y = element_blank())

p1 + p2

3. By site and topic

sentDF.red %>%
  group_by(site, topic_name, topic) %>%
  summarise(sent_value = mean(sent_value)) %>%
  ggplot(aes(reorder(topic_name, topic), sent_value)) +
  geom_col(fill=col[3], alpha=0.8) +
  coord_flip() +
  facet_wrap(~site, ncol = 7) +
  labs(x="", y="sentiment value (unweighted)",
       title = "sentiment value (unweighted)") +
  theme(axis.text.y = element_text(size=12))

ggsave(filename = "../figs/sent_weighted.png", device = "png",width = 12, height = 8,
dpi = 600)
sentDF.red %>%
  group_by(site, topic_name, topic) %>%
  summarise(weighted = mean(weighted)) %>%
  ggplot(aes(reorder(topic_name, topic), weighted)) +
  geom_col(fill=col[1], alpha=0.8) +
  coord_flip() +
  facet_wrap(~site, ncol = 7) +
  labs(x="", y="sentiment value (weighted)",
       title = "sentiment value (weighted)") +
  theme(axis.text.y = element_text(size=12))

Radar plot

require(ggiraph)
require(ggiraphExtra)

Select topics to be analyzed:

1: “SPD, M.Schulz” 2: “A.Merkel vs. Schulz” 3: “Jamaica coalition” 6: “A.Merkel” 8: “CSU, Seehofer, Söder” 14: “B90/Die Grüne” 16: “Great coalition” 17: “AfD, in social media”, 24: “CDU, social media” 29: “AfD”

1, “SPD, M.Schulz” 2, “A.Merkel vs. Schulz” 3, “B90/Die Grüne, coalition talks” 4, “EU policies” 5, “H.Kohl”, 6, “A.Merkel”, 8, “CSU, Seehofer, Söder”, 12, “Refugees in GER” 14, “B90/Die Grüne, climate/refugee policy”, 16, “Great coalition”, 17, “AfD, in media”, 18, “Jamaica coalition”, 29, “AfD”, 33, “Diplomatic relations w Turkey, Russia”, 35, “CDU/CSU H.Seehofer, Obergrenze”, 38, “AfD, F.Petry”)

#keep <- c(1,2,3,6,8,14,16,17,24,29)

keep <- c(1,3,4,5,6,8,14,16,17,18,29,33,35,38)

Unweighted

sentDF.red %>% 
  filter(topic %in% keep) %>%
  group_by(site, topic_name) %>%
  summarise(sent_value = mean(sent_value)) %>%
  spread(key=topic_name, value=sent_value) %>%
  ungroup() -> radar

radar %>%
  ggRadar(aes(color=site), rescale = F,
          alpha = 0, legend.position = "right") +
  labs(title = "")
## Warning: Removed 2 rows containing missing values
## (geom_interactive_polygon).
## Warning: Removed 2 rows containing missing values (geom_interactive_point).

Weighted

sentDF.red %>% 
  filter(topic %in% keep) %>%
  group_by(site, topic_name) %>%
  summarise(weighted = mean(weighted)) %>%
  spread(key=topic_name, value=weighted) %>%
  ungroup() -> radar

radar %>%
  ggRadar(aes(color=site), rescale = F,
          alpha = 0, legend.position = "right") +
  labs(title = "")
## Warning: Removed 2 rows containing missing values
## (geom_interactive_polygon).
## Warning: Removed 2 rows containing missing values (geom_interactive_point).